#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <cstdlib>
#include <mysql/mysql.h>
#include <mutex>

namespace order_sys{

#define DB_HOST "192.168.107.130" //主机IP地址
#define DB_USER "root"            //登录MySQL的用户名
#define DB_NAME "order_system"    //使用的库
  //返回一个初始化完成的Mysql操作句柄
  MYSQL* MysqlInit(){
    //1. 初始化句柄
    MYSQL* mysql = mysql_init(NULL);//通常传参传NULL
    if(mysql == NULL){
      std::cout << "mysql init error!\n";
      return NULL;
    }
    //2. 连接数据库服务器
    if(mysql_real_connect(mysql, DB_HOST, DB_USER, NULL, DB_NAME, 0, NULL, 0) == NULL){ 
      printf("connect mysql server failed! : %s\n", mysql_error(mysql));
      mysql_close(mysql);
      return NULL;
    }
    //3. 设置字符集
    int ret;
    ret = mysql_set_character_set(mysql, "utf8");
    if(ret != 0){
      printf("set character failed! : %s\n", mysql_error(mysql));
      mysql_close(mysql);
      return NULL;
    }
    //4. 返回句柄
    return mysql;
  }
  //释放销毁数据库操作句柄
  void MysqlDestory(MYSQL* mysql){
    if(mysql != NULL){
      mysql_close(mysql);
    }
    return ;
  }
  //Mysql语句执行接口
  bool MysqlQuery(MYSQL* mysql, const std::string& sql){
    int ret = mysql_query(mysql, sql.c_str());
    if(ret != 0){
      std::cout << sql << std::endl;
      std::cout << "query failed! " << std::endl;
      return false;
    }
    return true;
  }
  /////订单类/////
  class TableDish{
    private:
      std::mutex _mutex; //定义一个互斥锁
      MYSQL* _mysql;
    public:
      //初始化数据库操作句柄
      TableDish():_mysql(NULL) {
        _mysql = MysqlInit();
        if(_mysql == NULL){
          exit(-1);
        }
      }
      //销毁数据库操作句柄
      ~TableDish(){
        MysqlDestory(_mysql);
      }
      //dish传入要新增的菜品信息
      bool Insert(const Json::Value &dish){
        //id, name, price, info, ctime
        //insert tb_dish values(null, '红烧肉', 1800, '红色的肉', now());
        if(dish["name"].empty() == true || dish["name"].asString().size() == 0){
          std::cout << " new dish name is not exists!\n";
          return false;
        }
        if(dish["price"].empty() == true || dish["price"].asInt() <= 0){
          std::cout << " new dish price is error!\n";
          return false;
        }
        if(dish["info"].empty() == true || dish["info"].asString().size() == 0){
          std::cout << " new dish info is null!\n"; 
          return false;
        }

        char sql[4096] = {0};
#define INSERT_DISH "insert tb_dish values(null, '%s', %d, '%s', now());"  //定义一个宏
        sprintf(sql, INSERT_DISH, dish["name"].asCString(), dish["price"].asInt(), dish["info"].asCString()); //将数据写入到sql中
        return MysqlQuery(_mysql, sql); //调用查询语句
      }
      // 将dish_id对应的菜品信息修改为新的dish菜品信息
      bool Update(int dish_id, const Json::Value &dish){

        char sql[4096] = {0};
#define UPDATE_DISH "update tb_dish set name='%s', price=%d, info='%s', ctime=now() where id=%d;"
        sprintf(sql, UPDATE_DISH, dish["name"].asCString(), dish["price"].asInt(), dish["info"].asCString(), dish_id);
        return MysqlQuery(_mysql, sql);
      }
      //删除菜品信息
      bool Delete(int dish_id){
#define DELETE_DISH "delete from tb_dish where id=%d;"
        char sql[4096] = {0};
        sprintf(sql, DELETE_DISH, dish_id);
        return MysqlQuery(_mysql, sql);
      }
      //dishes作为输出参数，返回所有的菜品信息
      bool GetAll(Json::Value* dishes){
#define SELECT_ALLDISH "select id, name, price, info, ctime from tb_dish;"
        _mutex.lock(); // 查询语句之前加锁
        bool ret = MysqlQuery(_mysql, SELECT_ALLDISH);
        if(ret == false){
          _mutex.unlock();  //在任意有可能退出的地方解锁
          return false;
        }

        MYSQL_RES* res = mysql_store_result(_mysql);
        if(res == NULL){
          printf("store result failed! : %s\n", mysql_error(_mysql));
          _mutex.unlock();  //解锁
          return false; 
        }
        _mutex.unlock(); //查询结果保存到本地后解锁
        int num_row = mysql_num_rows(res);
        if(num_row < 1){
          std::cout << "have no dishes!" << std::endl; //行数小于1就出问题了
          return false;
        }
        for(int i = 0; i < num_row; ++i){
          MYSQL_ROW row = mysql_fetch_row(res); //获取一行的数据
          Json::Value dish;
          dish["id"] = atoi(row[0]);
          dish["name"] = row[1];
          dish["price"] = atoi(row[2]);
          dish["info"] = row[3];
          dish["ctime"] = row[4];
          dishes->append(dish);//将单个菜品信息作为数组元素添加到输出参数dishes中
        }
        // 释放结果集
        mysql_free_result(res);
        return true;
      }
      //返回指定的菜品信息
      bool GetOne(int dish_id, Json::Value* dish){
#define SELECT_ONEDISH "select name, price, info, ctime from tb_dish where id=%d;"
        char sql[4096]={0};
        sprintf(sql, SELECT_ONEDISH, dish_id);
        _mutex.lock();//加锁原理和上一个一样
        bool ret = MysqlQuery(_mysql, sql);
        if(ret == false){
          _mutex.unlock();
          return false;
        }

        MYSQL_RES* res = mysql_store_result(_mysql);
        if(res == NULL){
          printf("store result failed! : %s\n", mysql_error(_mysql));
          _mutex.unlock();
          return false; 
        }
        _mutex.unlock();

        int num_row = mysql_num_rows(res);
        if(num_row < 1){
          std::cout << "have no dish!" << dish_id << std::endl; //行数小于1就出问题了
          return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        (*dish)["id"] = dish_id;
        (*dish)["name"] = row[0];
        (*dish)["price"] = atoi(row[1]);
        (*dish)["info"] = row[2];
        (*dish)["ctime"] = row[3];
        // 释放结果集
        mysql_free_result(res);
        return true;
      }
  };
  //分割线

  class TableOrder{
    private:
      std::mutex _mutex;
      MYSQL* _mysql;
    public:
      //初始化数据库操作句柄
      TableOrder():_mysql(NULL) {
        _mysql = MysqlInit();
        if(_mysql == NULL){
          exit(-1);
        }
      }
      //销毁数据库操作句柄
      ~TableOrder(){
        MysqlDestory(_mysql);
      }
      //order传入要新增的订单信息
      bool Insert(const Json::Value &order){
        //id, table_id, ctime, mtime, dishes, status
        //insert tb_order values(null,'1',now(),now(),'[1,2]',0);
        //判断传入条件
          if(order["table_id"].empty() == true || order["table_id"].asString().size() == 0){
          std::cout << " table_id not exists!\n";
          return false;
          }
        if(order["dishes"].empty() == true || order["dishes"].asString().size() == 0){
          std::cout << " new order dishes error!\n"; 
          return false;
          }
       
        
        char sql[4096] = {0};
#define INSERT_ORDER "insert tb_order values(null,'%s', now(), now(), '%s', 0);"
        /*
        // order["dishes"] 这里存放的是一个序列化完成的菜品id的json字符串，
        // 为了防备外界传入的菜品信息是一个json数组，而不是序列化后的字符串
        std::string dishes;
        Json::FastWriter writer;
        if(order["dishes"].isArray()) {
          dishes = writer.write(order["dishes"]);
          if(dishes[dishes.size() - 1 == '\n'])
            dishes[dishes.size() - 1] = '\0';
        }
        else {
          dishes = order["dishes"].asString();
        }
        sprintf(sql, INSERT_ORDER, order["table_id"].asCString(), dishes.c_str());
        */
        sprintf(sql, INSERT_ORDER, order["table_id"].asCString(), order["dishes"].asCString());
        return MysqlQuery(_mysql, sql);
      }
      // 将order_id对应的订单状态修改为新的状态
      bool Update(int order_id, const Json::Value &order){

        char sql[4096] = {0};
#define UPDATE_ORDER "update tb_order set mtime=now(), status=%d where id=%d;"
        sprintf(sql, UPDATE_ORDER, order["status"].asInt(), order_id);
        return MysqlQuery(_mysql, sql);
      }
      //删除订单，一个桌子可以有多个订单
      bool Delete(int order_id){
#define DELETE_ORDER "delete from tb_order where id=%d;"
        char sql[4096] = {0};
        sprintf(sql, DELETE_ORDER, order_id);
        return MysqlQuery(_mysql, sql);
      }
      //orderes作为输出参数，返回所有的订单信息
      bool GetAll(Json::Value* orderes){
#define SELECT_ALLORDER "select id, table_id, ctime, mtime, dishes, status from tb_order;"
        _mutex.lock();
        bool ret = MysqlQuery(_mysql, SELECT_ALLORDER);
        if(ret == false){
          _mutex.unlock();
          return false;
        }

        MYSQL_RES* res = mysql_store_result(_mysql);
        if(res == NULL){
          printf("store result failed! : %s\n", mysql_error(_mysql));
          _mutex.unlock();
          return false; 
        }
        _mutex.unlock();
        int num_row = mysql_num_rows(res);
        if(num_row < 1){
          std::cout << "have no orders!" << std::endl;
          return false;
        }
        for(int i = 0; i < num_row; ++i){
          MYSQL_ROW row = mysql_fetch_row(res);
          Json::Value order;
          order["id"] = atoi(row[0]);
          order["table_id"] = row[1];
          order["ctime"] = (row[2]);
          order["mtime"] = row[3];
          order["dishes"] = row[4];
          order["status"] =atoi(row[5]);
          orderes->append(order);//将单个订单信息作为数组元素添加到输出参数orderes中
        }
        // 释放结果集
        mysql_free_result(res);
        return true;
      }
      //返回指定的订单信息
      bool GetOne(int order_id, Json::Value* order){
#define SELECT_ONEORDER "select table_id, ctime, mtime, dishes, status from tb_order where id=%d;"
        char sql[4096]={0};
        sprintf(sql, SELECT_ONEORDER, order_id);
        _mutex.lock();
        bool ret = MysqlQuery(_mysql, sql);
        if(ret == false){
          _mutex.unlock();
          return false;
        }

        MYSQL_RES* res = mysql_store_result(_mysql);
        if(res == NULL){
          printf("store result failed! : %s\n", mysql_error(_mysql));
          _mutex.unlock();
          return false; 
        }
        _mutex.unlock();

        int num_row = mysql_num_rows(res);
        if(num_row < 1){
          std::cout << "have no order!" << order_id << std::endl;
          return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        (*order)["id"] = order_id;
        (*order)["table_id"] = row[0];
        (*order)["ctime"] = row[1];
        (*order)["mtime"] = row[2];
        (*order)["dishes"] = row[3];
        (*order)["status"] = atoi(row[4]);
        // 释放结果集
        mysql_free_result(res);
        return true;
      }
  };

}
